<?php

declare(strict_types=1);

/*
 * This file is part of blty package.
 *
 * (c) blty.com
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace KDD\SDK;

use GuzzleHttp\Client as HttpClient;
use GuzzleHttp\Promise;
use Illuminate\Config\Repository;
use function KDD\Helper\hydractor;

class Client
{
    /**
     * @var self
     */
    private static $self;

    /**
     * @var Repository
     */
    private $config;

    private $isInitial = false;

    /**
     * @var HttpClient
     */
    private $httpClient;

    private function __construct(Repository $config)
    {
        $this->config = $config;
    }

    public static function initial(Repository $config): void
    {
        if (null == self::$self) {
            self::$self = new self($config);
        }
    }

    /**
     * @param string      $uri
     * @param array       $params
     * @param string|null $type
     *
     * @return array|float|int|string|bool|null
     */
    public static function requestApi(string $uri, array $params, string $type = null)
    {
        $promise = self::requestApiAsync($uri, $params);
        /* @var \GuzzleHttp\Psr7\Response $response */
        $response = $promise->wait();
        $body = json_decode((string) $response->getBody(), true);
        $return = null;
        if (200 != $body['code']) {
            throw new $body['class']($body['error'], $body['hint']);
        }
        switch ($type) {
            case 'array':
            case 'bool':
            case 'float':
            case 'int':
            case 'string':
                settype($body['data'], $type);
                $return = $body['data'];
                break;
            case 'void':
                break;
            default:
                $return = hydractor($body['data'], $type);
        }

        return $return;
    }

    public static function requestApiAsync(string $uri, array $params): Promise\PromiseInterface
    {
        $client = self::$self;
        if (!$client->isInitial()) {
            $appConfig = $client->getConfig();
            $client->setHttpClient(new HttpClient([
                'base_uri' => $appConfig['sdk']['gateway'],
                'verify'   => \defined('APP') && 'prod' == APP['env'],
            ]));
            $client->setIsInitial(true);
        }
        $httpClient = $client->getHttpClient();
        $options = [
            'json' => $params,
        ];
        $promise = $httpClient->postAsync($uri, $options);

        return $promise;
    }

    /**
     * @return mixed
     */
    public function getConfig()
    {
        return $this->config;
    }

    /**
     * @return bool
     */
    public function isInitial(): bool
    {
        return $this->isInitial;
    }

    /**
     * @param bool $isInitial
     */
    public function setIsInitial(bool $isInitial): void
    {
        $this->isInitial = $isInitial;
    }

    /**
     * @return HttpClient
     */
    public function getHttpClient()
    {
        return $this->httpClient;
    }

    /**
     * @param HttpClient $httpClient
     */
    public function setHttpClient($httpClient): void
    {
        $this->httpClient = $httpClient;
    }
}
